import { Readable } from "stream";
import * as http from "http";
import * as https from "https";
import { URL } from "url";
import HadaProxy from "./Proxy";

export default class Request {
	body?: Buffer;
	url!: string;
	method: string | undefined;
	headers: http.IncomingHttpHeaders;
	_source: http.IncomingMessage;
	sendRaw!: NodeJS.ReadableStream;
	private _hadaProxy: HadaProxy;

	sendToServer(): Promise<http.IncomingMessage> {
		const url = new URL(this.url);

		return new Promise((resolve, reject) => {
			if (this.body) {
				delete this.headers["content-length"];
			}
			// 如果有设置上游代理 这边需要转换请求
			const opts = {
				hostname: url.hostname,
				// protocol: url.protocol,
				port: url.port,
				method: this.method,
				path: url.pathname + url.search,
				headers: this.headers,
			};
			// console.log(opts.hostname, opts.port, opts.path);
			//上游服务器设置
			const upstreamProxy = this._hadaProxy.upstreamProxy;
			let client = url.protocol === "http:" ? http : https;
			if (upstreamProxy) {
				let proxyInfo = new URL(upstreamProxy);
				client = proxyInfo.protocol === "http:" ? http : https;
				let proxyPort = proxyInfo.port,
					proxyHostname = proxyInfo.hostname,
					proxyPath =
						url.protocol +
						"//" +
						opts.hostname +
						(opts.port ? ":" + opts.port : "") +
						opts.path;
				opts.hostname = proxyHostname;
				opts.port = proxyPort;
				opts.path = proxyPath;
			}

			let req = client.request(opts, (res) => {
				resolve(res);
			});
			req.on("error", (err) => {
				// console.log("request error", err);
				reject(err);
			});
			const readable = this.body
				? Readable.from(this.body)
				: this.sendRaw;
			readable.pipe(req);
			// req.end();
		});
	}

	constructor(_source: http.IncomingMessage, hadaProxy: HadaProxy) {
		this._source = _source;
		this.url = _source.url ?? "";
		this.headers = _source.headers;
		this.method = _source.method;
		this.sendRaw = _source;
		this._hadaProxy = hadaProxy;
	}

	readBody(): Promise<void> {
		return new Promise((resolve, reject) => {
			let body: Buffer[] = [];
			this._source.on("error", (err) => {
				reject(err);
			});
			this._source.on("data", (chunk: any) => {
				body.push(chunk);
			});
			this._source.on("end", () => {
				this.body = Buffer.concat(body);
				resolve();
			});
		});
	}
}
